﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Bonjour;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Threading;
//using System.Text;


namespace OfficeServer
{
    public partial class MainForm : Form
    {
        private const int MAX_CONNECTION = 30;

        private MessageDispatcher m_dispatcher = null;
        private ArrayList _mobiles = new ArrayList(MAX_CONNECTION);

        private Bonjour.DNSSDEventManager       m_eventManager = null;
        private Bonjour.DNSSDService            m_service = null;
        private Bonjour.DNSSDService            m_registrar = null;
        private Bonjour.DNSSDService            m_browser = null;
        private Bonjour.DNSSDService            m_resolver = null;
		private String					        m_name;        
        delegate void                           ReadMessageCallback(String data,Socket handler);
        ReadMessageCallback                     m_readMessageCallback;
        Thread piThread = null;
        private MobileInfo _mobileInfo = null;
        
        public static ManualResetEvent allDone = new ManualResetEvent(false);


		/// <summary>
		/// Required designer variable.
		/// </summary>
		//private System.ComponentModel.Container components = null;
		//private System.Windows.Forms.RichTextBox richTextBox1;

        public bool getChkAutoSubmit()
        {
            return this.chkAutoSubmit.Checked;
        }

        public void setLbIp2(String str)
        {
            lbIP2.Text = str;
        }

        public void setMobileTime(String str){
            lbMobileTime.Text = str;
        }

        public MainForm()
        {
            //
            // Required for Windows Form Designer support
            //
            InitializeComponent();
            m_dispatcher = new MessageDispatcher(_mobileInfo, this);

            try
            {
                m_service = new DNSSDService();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Bonjour Service is not available "+ex.ToString(), "Error");
                Application.Exit();
            }

            m_eventManager = new DNSSDEventManager();
            m_eventManager.ServiceRegistered += new _IDNSSDEvents_ServiceRegisteredEventHandler(this.ServiceRegistered);
            m_eventManager.ServiceFound += new _IDNSSDEvents_ServiceFoundEventHandler(this.ServiceFound);
            m_eventManager.ServiceLost += new _IDNSSDEvents_ServiceLostEventHandler(this.ServiceLost);
            m_eventManager.ServiceResolved += new _IDNSSDEvents_ServiceResolvedEventHandler(this.ServiceResolved);
            m_eventManager.QueryRecordAnswered += new _IDNSSDEvents_QueryRecordAnsweredEventHandler(this.QueryAnswered);
            m_eventManager.OperationFailed += new _IDNSSDEvents_OperationFailedEventHandler(this.OperationFailed);

            m_readMessageCallback = new ReadMessageCallback(OnReadMessage);

            //this.Load += new System.EventHandler(this.Form1_Load);

            this.AcceptButton = btnOK;
        }


		// ServiceRegistered
		//
		// Called by DNSServices core as a result of Register()
		// call
		//

        public void ServiceRegistered(DNSSDService service, DNSSDFlags flags,String name, String regType, String domain )
        {
            m_name = name;

            try
            {
                m_browser = m_service.Browse(0, 0, "_ims._udp", null, m_eventManager);
            }
            catch
            {
                MessageBox.Show("Browse Failed", "Error");
                Application.Exit();
            }
        }

		//
		// ServiceFound
		//
		// Called by DNSServices core as a result of a Browse call
		//

		public void ServiceFound
            (DNSSDService sref,DNSSDFlags flags,uint ifIndex,String serviceName, String regType,String domain)
		{
            if (serviceName != m_name)
            {
                PeerData peer = new PeerData();

                peer.InterfaceIndex = ifIndex;
                peer.Name = serviceName;
                peer.Type = regType;
                peer.Domain = domain;
                peer.Address = null;

                lbClients.Items.Add(peer);

                if (lbClients.Items.Count == 1)
                {
                    lbClients.SelectedIndex = 0;
                }
            }
		}

        //
        // ServiceLost
        //
        // Called by DNSServices core as a result of a Browse call
        //

        public void ServiceLost
            (DNSSDService sref, DNSSDFlags flags, uint ifIndex, String serviceName, String regType, String domain)
        {
            PeerData peer = new PeerData();

            peer.InterfaceIndex = ifIndex;
            peer.Name = serviceName;
            peer.Type = regType;
            peer.Domain = domain;
            peer.Address = null;

            lbClients.Items.Remove(peer);
        }

		//
		// ServiceResolved
		//
		// Called by DNSServices core as a result of DNSService.Resolve()
		// call
		//

        public void ServiceResolved
            (DNSSDService sref, DNSSDFlags flags, uint ifIndex, String fullName, String hostName, ushort port,TXTRecord txtRecord)
		{
            m_resolver.Stop();
            m_resolver = null;

            PeerData peer = (PeerData)lbClients.SelectedItem;

            peer.Port = port;

            try
            {
                m_resolver = m_service.QueryRecord(0, ifIndex, hostName, DNSSDRRType.kDNSSDType_A, DNSSDRRClass.kDNSSDClass_IN, m_eventManager );
            }
            catch
            {
                MessageBox.Show("QueryRecord Failed", "Error");
                Application.Exit();
            }
		}

		//
		// QueryAnswered
		//
		// Called by DNSServices core as a result of DNSService.QueryRecord()
		// call
		//

		public void QueryAnswered
            (DNSSDService service, DNSSDFlags flags, uint ifIndex, String fullName, DNSSDRRType rrtype, DNSSDRRClass rrclass, Object rdata, uint ttl)
        {
            m_resolver.Stop();
            m_resolver = null;

            PeerData peer = (PeerData) lbClients.SelectedItem;

			uint bits = BitConverter.ToUInt32( (Byte[])rdata, 0);
			System.Net.IPAddress address = new System.Net.IPAddress(bits);

            peer.Address = address;
		}

        public void OperationFailed(DNSSDService service, DNSSDError error)
        {
            MessageBox.Show("Operation returned an error code " + error, "Error");
        }

        //
        // OnReadMessage
        //
        // Called when there is data to be read on a socket
        //
        // This is called (indirectly) from OnReadSocket()
        //
        private void OnReadMessage(String msg, Socket handler)                
        {                        
            //richTextBox1.SelectionColor = color;
            //richTextBox1.AppendText(msg + Environment.NewLine);
            txtMessageBox.AppendText("Received: "+msg + Environment.NewLine);
            String res = m_dispatcher.runImmediately(msg, handler,_mobileInfo);
            if (res == null)
                return;
            
            txtMessageBox.AppendText("Processed: " + res + Environment.NewLine);
            String[] results = res.Split(Constant.SEPERATOR_CHAR);            
            if (results[0] == Constant.REQUESTCONNECTING.ToString())
            {
                MessageBox.Show("Paircode: "+results[1]);
            }
            if (results[0] == Constant.REQUESTPAIRCODE.ToString())
            {
                lbIP2.Text = results[1];
                lbMobileStatus.Text = "Connected";
            }
        }
		//
		// OnReadSocket
		//
		// Called by the .NET core when there is data to be read on a socket
		//
		// This is called from a worker thread by the .NET core
		//
		private void OnReadSocket(IAsyncResult ar)
		{
            
            String content = String.Empty;

            // Retrieve the state object and the handler socket
            // from the asynchronous state object.
            StateObject state = (StateObject)ar.AsyncState;
            Socket handler = state.m_socket;

            // Read data from the client socket. 
            int bytesRead = handler.EndReceive(ar);

            if (bytesRead > 0)
            {
                // There  might be more data, so store the data received so far.
                
                state.m_sb.Append(Encoding.ASCII.GetString(
                    state.m_buffer, 0, bytesRead));
                // Check for end-of-file tag. If it is not there, read 
                // more data.
                content = state.m_sb.ToString();
                if (content.IndexOf("<EOF>") > -1)
                {
                    // All the data has been read from the 
                    // client. Display it on the console.
                    Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
                        content.Length, content);
                    Invoke(m_readMessageCallback, new Object[] { content,handler });
                    // Echo the data back to the client.
                    //Send(handler, content);
                }
                else
                {
                    // Not all data received. Get more.
                    handler.BeginReceive(state.m_buffer, 0, StateObject.BUFFER_SIZE, 0,
                    new AsyncCallback(OnReadSocket), state);
                }
            }
			
		}

        private bool SendListClients()
        {
            return true;
        }

        public void OnAcceptSocket(IAsyncResult ar)
        {
            // Signal the main thread to continue.
            allDone.Set();

            // Get the socket that handles the client request.
            Socket listener = (Socket)ar.AsyncState;
            Socket handler = listener.EndAccept(ar);

            // Create the state object.
            StateObject state = new StateObject();
            state.m_socket = handler;
            handler.BeginReceive(state.m_buffer, 0, StateObject.BUFFER_SIZE, 0, new AsyncCallback(OnReadSocket), state);
            //handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
            //    new AsyncCallback(ReadCallback), state);
        }

        private void startListen()
        {
            // create the socket and bind to INADDR_ANY
            

            IPEndPoint localEP = new IPEndPoint(System.Net.IPAddress.Any, Constant.SERVER_PORT);
            Socket listener = new Socket(AddressFamily.InterNetwork,
            SocketType.Stream, ProtocolType.Tcp);
            listener.Bind(localEP);
            listener.Listen(100);
            
            
            try
            {
                //
                // start the register and browse operations
                //
                m_registrar = m_service.Register(0, 0, System.Environment.UserName, "_ims._udp", null, null, (ushort)localEP.Port, null, m_eventManager);
                while (true)
                {
                    // Set the event to nonsignaled state.
                    allDone.Reset();

                    // Start an asynchronous socket to listen for connections.
                    //Console.WriteLine("Waiting for a connection...");
                    
                    listener.BeginAccept(
                        new AsyncCallback(this.OnAcceptSocket),
                        listener);

                    // Wait until a connection is made before continuing.
                    allDone.WaitOne();
                }
            }
            catch (Exception ex)
            {
                ex.ToString();
            }

        }

		private void MainForm_Load(object sender, EventArgs e) 
		{            
            try
            {
                // @khangvn: ??? not good here
                
                String hostname = Dns.GetHostName();
                lbHostName.Text = hostname;
                IPHostEntry ipEntry = System.Net.Dns.GetHostEntry(hostname);
                IPAddress[] addr = ipEntry.AddressList;
                lbIP1.Text = addr[addr.Length - 1].ToString();                

                Thread piThread = new Thread(new ThreadStart(this.startListen));
                piThread.Start();
                
            }
            catch
            {
                MessageBox.Show("Bonjour service is not available", "Error");
                Application.Exit();
            }
			
		}
		

		//
		// called when typing in message box
		//
		private void txtMessage_TextChanged(object sender, System.EventArgs e)
		{
			PeerData peer = (PeerData) lbClients.SelectedItem;
            btnOK.Enabled = ((peer.Address != null) && (txtMessage.Text.Length > 0));
		}

		//
		// called when peer target changes
		//
		/// <summary>
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void lbClients_SelectedIndexChanged(object sender, System.EventArgs e)
		{
            
			PeerData peer = (PeerData) lbClients.SelectedItem;

			try
			{
                if (peer != null)
                    m_resolver = m_service.Resolve(0, peer.InterfaceIndex, peer.Name, peer.Type, peer.Domain, m_eventManager);
                else
                {
                    if (lbClients.Items.Count != 0)
                        lbClients.SetSelected(lbClients.Items.Count-1,true);
                }

			}
			catch
			{
				MessageBox.Show("Unable to Resolve service", "Error");
				Application.Exit();
			}
		}

        //
        // send the message to a peer
        //
        private void btnOK_Click(object sender, EventArgs e)
        {
            /*
            PeerData peer = (PeerData)lbClients.SelectedItem;

            String message = m_name + ": " + txtMessage.Text;

            Byte[] bytes = Encoding.UTF8.GetBytes(message);

            IPEndPoint endPoint = new IPEndPoint(peer.Address, peer.Port);

            Socket tempSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            tempSocket.SendTo(bytes, endPoint);

            //txtMessageBox.SelectionColor = Color.Black;

            txtMessageBox.AppendText(txtMessage.Text + "\n");

            txtMessage.Text = "";
             */
 
        }


        public delegate void AddMessageDelegate(String text, bool messageBox);

        public void addMessage(String str, bool messageBox)
        {
            if (this.txtMessageBox.InvokeRequired)
            {
                // This is a worker thread so delegate the task.
                this.txtMessage.Invoke(new AddMessageDelegate(this.addMessage), new object[] { str, messageBox });
            }
            else
            {
                if (!messageBox)
                    txtMessageBox.Text += str + " \r\n";
                else
                    MessageBox.Show(str);
            }
        }




        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }

                if (m_registrar != null)
                {
                    m_registrar.Stop();
                }

                if (m_browser != null)
                {
                    m_browser.Stop();
                }

                if (m_resolver != null)
                {
                    m_resolver.Stop();
                }

                m_eventManager.ServiceFound -= new _IDNSSDEvents_ServiceFoundEventHandler(this.ServiceFound);
                m_eventManager.ServiceLost -= new _IDNSSDEvents_ServiceLostEventHandler(this.ServiceLost);
                m_eventManager.ServiceResolved -= new _IDNSSDEvents_ServiceResolvedEventHandler(this.ServiceResolved);
                m_eventManager.QueryRecordAnswered -= new _IDNSSDEvents_QueryRecordAnsweredEventHandler(this.QueryAnswered);
                m_eventManager.OperationFailed -= new _IDNSSDEvents_OperationFailedEventHandler(this.OperationFailed);

            }
            base.Dispose(disposing);
        }

        private void groupBox2_Enter(object sender, EventArgs e)
        {

        }

        private void label6_Click(object sender, EventArgs e)
        {

        }

        private void btnLogout_Click(object sender, EventArgs e)
        {
            if (m_dispatcher == null)
                return;
            MobileInfo info = m_dispatcher.getMobileInfo();
            if (info != null)
            {
                info.Port = 0;
                info.Ip = null;
                info.SessionId = null;
                info.Status = Constant.STATUS_NOT_CONNECTED;
                info.CodeValidation = "";
                info.LastConnect = DateTime.MinValue;
                txtMessage.Text = "";
                lbIP2.Text = "#";
                lbMobileTime.Text = "#";
            }
        }
        
    }

    public class StateObject
    {
        public Socket m_socket = null;
        public const int BUFFER_SIZE = 1024;
        public byte[] m_buffer = new byte[BUFFER_SIZE];
        public bool m_complete = false;
        public StringBuilder m_sb = new StringBuilder();
    }

}
